]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
shell-screenshot: Change to use clutter_stage_paint_to*() API
authorJonas Ådahl <jadahl@gmail.com>
Thu, 30 Jul 2020 07:51:06 +0000 (09:51 +0200)
committerGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>
Thu, 6 Aug 2020 16:26:59 +0000 (16:26 +0000)
This eliminates the need to wait for redraws, drawing cursors, and
stiching together cairo images in case the screenshot covers multiple
monitors.

All of that is now handled by mutter itself.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1383

src/shell-screenshot.c

index 36ca2fb24b131d819ed7b94501778762bcb6df58..3a1c92ccec4fd19aed99ceb104a731e7a6a9a39f 100644 (file)
 #include "shell-screenshot.h"
 #include "shell-util.h"
 
+typedef enum _ShellScreenshotFlag
+{
+  SHELL_SCREENSHOT_FLAG_NONE,
+  SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR,
+} ShellScreenshotFlag;
+
 typedef struct _ShellScreenshotPrivate  ShellScreenshotPrivate;
 
 struct _ShellScreenshot
@@ -32,7 +38,6 @@ struct _ShellScreenshotPrivate
   cairo_surface_t *image;
   cairo_rectangle_int_t screenshot_area;
 
-  gboolean include_cursor;
   gboolean include_frame;
 };
 
@@ -45,10 +50,6 @@ typedef enum
 
 G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
 
-static void
-grab_screenshot (ClutterActor *stage,
-                 GTask        *result);
-
 static void
 shell_screenshot_class_init (ShellScreenshotClass *screenshot_class)
 {
@@ -77,8 +78,6 @@ on_screenshot_written (GObject      *source,
   g_clear_pointer (&priv->image, cairo_surface_destroy);
   g_clear_object (&priv->stream);
   g_clear_pointer (&priv->datetime, g_date_time_unref);
-
-  meta_enable_unredirect_for_display (shell_global_get_display (priv->global));
 }
 
 static void
@@ -121,63 +120,49 @@ write_screenshot_thread (GTask        *result,
 }
 
 static void
-do_grab_screenshot (ShellScreenshot *screenshot,
-                    ClutterStage    *stage,
-                    int               x,
-                    int               y,
-                    int               width,
-                    int               height)
+do_grab_screenshot (ShellScreenshot     *screenshot,
+                    int                  x,
+                    int                  y,
+                    int                  width,
+                    int                  height,
+                    ShellScreenshotFlag  flags)
 {
   ShellScreenshotPrivate *priv = screenshot->priv;
+  ClutterStage *stage = shell_global_get_stage (priv->global);
   cairo_rectangle_int_t screenshot_rect = { x, y, width, height };
-  ClutterCapture *captures;
-  int n_captures;
-  int i;
-
-  if (!clutter_stage_capture (stage, FALSE,
-                              &screenshot_rect,
-                              &captures,
-                              &n_captures))
-    return;
-
-  if (n_captures == 1)
-    priv->image = cairo_surface_reference (captures[0].image);
+  int image_width;
+  int image_height;
+  float scale;
+  cairo_surface_t *image;
+  ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
+  g_autoptr (GError) error = NULL;
+
+  clutter_stage_get_capture_final_size (stage, &screenshot_rect,
+                                        &image_width,
+                                        &image_height,
+                                        &scale);
+  image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                      image_width, image_height);
+
+  if (flags & SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR)
+    paint_flags |= CLUTTER_PAINT_FLAG_FORCE_CURSORS;
   else
+    paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
+  if (!clutter_stage_paint_to_buffer (stage, &screenshot_rect, scale,
+                                      cairo_image_surface_get_data (image),
+                                      cairo_image_surface_get_stride (image),
+                                      CLUTTER_CAIRO_FORMAT_ARGB32,
+                                      paint_flags,
+                                      &error))
     {
-      float target_scale;
-
-      clutter_stage_get_capture_final_size (stage, &screenshot_rect,
-                                            &width, &height, &target_scale);
-      priv->image = shell_util_composite_capture_images (captures,
-                                                         n_captures,
-                                                         x, y,
-                                                         width, height,
-                                                         target_scale);
+      cairo_surface_destroy (image);
+      g_warning ("Failed to take screenshot: %s", error->message);
+      return;
     }
 
-  priv->datetime = g_date_time_new_now_local ();
-
-  for (i = 0; i < n_captures; i++)
-    cairo_surface_destroy (captures[i].image);
+  priv->image = image;
 
-  g_free (captures);
-}
-
-static gboolean
-should_draw_cursor_image (ShellScreenshotMode mode)
-{
-  if (mode == SHELL_SCREENSHOT_WINDOW || !meta_is_wayland_compositor ())
-    {
-      StSettings *settings = st_settings_get ();
-      gboolean magnifier_active = FALSE;
-
-      g_object_get (settings, "magnifier-active", &magnifier_active, NULL);
-
-      if (!magnifier_active)
-        return TRUE;
-    }
-
-  return FALSE;
+  priv->datetime = g_date_time_new_now_local ();
 }
 
 static void
@@ -256,116 +241,37 @@ draw_cursor_image (cairo_surface_t       *surface,
 }
 
 static void
-on_paint (ClutterActor        *actor,
-          ClutterPaintContext *paint_context,
-          GTask               *result)
-{
-  grab_screenshot (actor, result);
-}
-
-static void
-on_actors_painted (ClutterActor *actor,
-                   GTask        *result)
-{
-  grab_screenshot (actor, result);
-}
-
-static void
-grab_screenshot (ClutterActor *stage,
-                 GTask        *result)
+grab_screenshot (ShellScreenshot     *screenshot,
+                 ShellScreenshotFlag  flags,
+                 GTask               *result)
 {
+  ShellScreenshotPrivate *priv = screenshot->priv;
   MetaDisplay *display;
   int width, height;
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
-  ShellScreenshotPrivate *priv = screenshot->priv;
   GTask *task;
 
   display = shell_global_get_display (priv->global);
   meta_display_get_size (display, &width, &height);
 
-  do_grab_screenshot (screenshot, CLUTTER_STAGE (stage), 0, 0, width, height);
-
-  if (meta_display_get_n_monitors (display) > 1)
-    {
-      cairo_region_t *screen_region = cairo_region_create ();
-      cairo_region_t *stage_region;
-      MetaRectangle monitor_rect;
-      cairo_rectangle_int_t stage_rect;
-      int i;
-      cairo_t *cr;
-
-      for (i = meta_display_get_n_monitors (display) - 1; i >= 0; i--)
-        {
-          meta_display_get_monitor_geometry (display, i, &monitor_rect);
-          cairo_region_union_rectangle (screen_region,
-                                        (const cairo_rectangle_int_t *) &monitor_rect);
-        }
-
-      stage_rect.x = 0;
-      stage_rect.y = 0;
-      stage_rect.width = width;
-      stage_rect.height = height;
-
-      stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect);
-      cairo_region_xor (stage_region, screen_region);
-      cairo_region_destroy (screen_region);
-
-      cr = cairo_create (priv->image);
-
-      for (i = 0; i < cairo_region_num_rectangles (stage_region); i++)
-        {
-          cairo_rectangle_int_t rect;
-          cairo_region_get_rectangle (stage_region, i, &rect);
-          cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
-          cairo_fill (cr);
-        }
-
-      cairo_destroy (cr);
-      cairo_region_destroy (stage_region);
-    }
+  do_grab_screenshot (screenshot,
+                      0, 0, width, height,
+                      flags);
 
   priv->screenshot_area.x = 0;
   priv->screenshot_area.y = 0;
   priv->screenshot_area.width = width;
   priv->screenshot_area.height = height;
 
-  if (priv->include_cursor)
-    draw_cursor_image (priv->image, priv->screenshot_area);
-
-  g_signal_handlers_disconnect_by_func (stage, on_paint, result);
-  g_signal_handlers_disconnect_by_func (stage, on_actors_painted, result);
-
   task = g_task_new (screenshot, NULL, on_screenshot_written, result);
   g_task_run_in_thread (task, write_screenshot_thread);
   g_object_unref (task);
 }
 
 static void
-grab_area_screenshot (ClutterActor *stage,
-                      GTask        *result)
+grab_window_screenshot (ShellScreenshot     *screenshot,
+                        ShellScreenshotFlag  flags,
+                        GTask               *result)
 {
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
-  ShellScreenshotPrivate *priv = screenshot->priv;
-  GTask *task;
-
-  do_grab_screenshot (screenshot,
-                      CLUTTER_STAGE (stage),
-                      priv->screenshot_area.x,
-                      priv->screenshot_area.y,
-                      priv->screenshot_area.width,
-                      priv->screenshot_area.height);
-
-  g_signal_handlers_disconnect_by_func (stage, grab_area_screenshot, result);
-  task = g_task_new (screenshot, NULL, on_screenshot_written, result);
-  g_task_run_in_thread (task, write_screenshot_thread);
-  g_object_unref (task);
-}
-
-static void
-grab_window_screenshot (ClutterActor *stage,
-                        GTask        *result)
-{
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
   ShellScreenshotPrivate *priv = screenshot->priv;
   GTask *task;
   MetaDisplay *display = shell_global_get_display (priv->global);
@@ -388,7 +294,7 @@ grab_window_screenshot (ClutterActor *stage,
                                              NULL);
   priv->datetime = g_date_time_new_now_local ();
 
-  if (priv->include_cursor)
+  if (flags & SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR)
     {
       if (meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_WAYLAND)
         {
@@ -401,33 +307,11 @@ grab_window_screenshot (ClutterActor *stage,
       draw_cursor_image (priv->image, priv->screenshot_area);
     }
 
-  g_signal_handlers_disconnect_by_func (stage, grab_window_screenshot, result);
   task = g_task_new (screenshot, NULL, on_screenshot_written, result);
   g_task_run_in_thread (task, write_screenshot_thread);
   g_object_unref (task);
 }
 
-static void
-grab_pixel (ClutterActor *stage,
-            GTask        *result)
-{
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
-  ShellScreenshotPrivate *priv = screenshot->priv;
-
-  do_grab_screenshot (screenshot,
-                      CLUTTER_STAGE (stage),
-                      priv->screenshot_area.x,
-                      priv->screenshot_area.y,
-                      1,
-                      1);
-
-  meta_enable_unredirect_for_display (shell_global_get_display (priv->global));
-
-  g_signal_handlers_disconnect_by_func (stage, grab_pixel, result);
-  g_task_return_boolean (result, TRUE);
-  g_object_unref (result);
-}
-
 static gboolean
 finish_screenshot (ShellScreenshot        *screenshot,
                    GAsyncResult           *result,
@@ -465,10 +349,9 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
                              GAsyncReadyCallback  callback,
                              gpointer             user_data)
 {
-  ClutterActor *stage;
   ShellScreenshotPrivate *priv;
-  gboolean use_paint_signal = FALSE;
   GTask *result;
+  ShellScreenshotFlag flags;
 
   g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
   g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
@@ -492,34 +375,12 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
   g_task_set_source_tag (result, shell_screenshot_screenshot);
 
   priv->stream = g_object_ref (stream);
-  priv->include_cursor = FALSE;
-
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
-
-  meta_disable_unredirect_for_display (shell_global_get_display (priv->global));
 
+  flags = SHELL_SCREENSHOT_FLAG_NONE;
   if (include_cursor)
-    {
-      if (should_draw_cursor_image (SHELL_SCREENSHOT_SCREEN))
-        priv->include_cursor = TRUE;
-      else
-        use_paint_signal = TRUE;
-    }
+    flags |= SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR;
 
-  if (use_paint_signal)
-    {
-      g_signal_connect_after (stage, "paint",
-                              G_CALLBACK (on_paint),
-                              result);
-    }
-  else
-    {
-      g_signal_connect_after (stage, "actors-painted",
-                              G_CALLBACK (on_actors_painted),
-                              result);
-    }
-
-  clutter_actor_queue_redraw (stage);
+  grab_screenshot (screenshot, flags, result);
 }
 
 /**
@@ -575,9 +436,9 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
                                   GAsyncReadyCallback  callback,
                                   gpointer             user_data)
 {
-  ClutterActor *stage;
   ShellScreenshotPrivate *priv;
   GTask *result;
+  g_autoptr (GTask) task = NULL;
 
   g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
   g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
@@ -606,13 +467,15 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
   priv->screenshot_area.width = width;
   priv->screenshot_area.height = height;
 
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
-
-  meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
-
-  g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_area_screenshot), result);
+  do_grab_screenshot (screenshot,
+                      priv->screenshot_area.x,
+                      priv->screenshot_area.y,
+                      priv->screenshot_area.width,
+                      priv->screenshot_area.height,
+                      SHELL_SCREENSHOT_FLAG_NONE);
 
-  clutter_actor_queue_redraw (stage);
+  task = g_task_new (screenshot, NULL, on_screenshot_written, result);
+  g_task_run_in_thread (task, write_screenshot_thread);
 }
 
 /**
@@ -666,7 +529,6 @@ shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
 {
   ShellScreenshotPrivate *priv;
   MetaDisplay *display;
-  ClutterActor *stage;
   MetaWindow *window;
   GTask *result;
 
@@ -695,16 +557,8 @@ shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
 
   priv->stream = g_object_ref (stream);
   priv->include_frame = include_frame;
-  priv->include_cursor = include_cursor &&
-                         should_draw_cursor_image (SHELL_SCREENSHOT_WINDOW);
-
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
 
-  meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
-
-  g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_window_screenshot), result);
-
-  clutter_actor_queue_redraw (stage);
+  grab_window_screenshot (screenshot, include_cursor, result);
 }
 
 /**
@@ -753,9 +607,7 @@ shell_screenshot_pick_color (ShellScreenshot     *screenshot,
                              gpointer             user_data)
 {
   ShellScreenshotPrivate *priv;
-  MetaDisplay *display;
-  ClutterActor *stage;
-  GTask *result;
+  g_autoptr (GTask) result = NULL;
 
   g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
 
@@ -769,14 +621,14 @@ shell_screenshot_pick_color (ShellScreenshot     *screenshot,
   priv->screenshot_area.width = 1;
   priv->screenshot_area.height = 1;
 
-  display = shell_global_get_display (priv->global);
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
-
-  meta_disable_unredirect_for_display (display);
-
-  g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_pixel), result);
+  do_grab_screenshot (screenshot,
+                      priv->screenshot_area.x,
+                      priv->screenshot_area.y,
+                      1,
+                      1,
+                      SHELL_SCREENSHOT_FLAG_NONE);
 
-  clutter_actor_queue_redraw (stage);
+  g_task_return_boolean (result, TRUE);
 }
 
 #if G_BYTE_ORDER == G_LITTLE_ENDIAN